<?php

namespace fastphp;

/**
 * 模型基类
 * 符合 MVC 规范的模型实现，负责业务逻辑处理和数据验证
 * 使用组合模式关联数据库操作，而非直接继承
 */
class Model
{
    /**
     * 模型名称
     * @var string
     */
    protected string $_model;

    /**
     * 数据表名
     * @var string
     */
    protected string $_table;

    /**
     * 数据库操作实例
     * @var Db
     */
    protected Db $_db;

    /**
     * 模型数据
     * @var array
     */
    protected array $_data = [];

    /**
     * 数据验证规则
     * @var array
     */
    protected array $_rules = [];

    /**
     * 验证错误信息
     * @var array
     */
    protected array $_errors = [];

    /**
     * 事件回调
     * @var array
     */
    protected array $_events = [];

    /**
     * 构造函数
     */
    function __construct()
    {
        // 初始化模型名称和表名
        if (empty($this->_table)) {
            // 转换模型+Model为模型名称
            $class_name = get_class($this);
            $class_name_split = explode('\\', $class_name);
            // 获取对象所属类的名称
            $this->_model = $class_name_split[count($class_name_split) - 1];
            $this->_model = rtrim($this->_model, 'Model');

            // 数据库表名与类名一致
            $this->_table = strtolower($this->_model);
        }

        // 初始化数据库实例
        $this->_db = new Db($this->_table);

        // 初始化事件
        $this->initEvents();
    }

    /**
     * 初始化事件
     */
    protected function initEvents(): void
    {
        // 可以被子类重写以注册自定义事件
    }

    /**
     * 注册事件回调
     * @param string $event 事件名称
     * @param callable $callback 回调函数
     * @return $this
     */
    public function on(string $event, callable $callback): Model
    {
        if (!isset($this->_events[$event])) {
            $this->_events[$event] = [];
        }
        $this->_events[$event][] = $callback;
        return $this;
    }

    /**
     * 触发事件
     * @param string $event 事件名称
     * @param array $params 事件参数
     * @return bool
     */
    protected function trigger(string $event, array $params = []): bool
    {
        if (!isset($this->_events[$event])) {
            return true;
        }

        foreach ($this->_events[$event] as $callback) {
            if (call_user_func_array($callback, [$this, ...$params]) === false) {
                return false;
            }
        }

        return true;
    }

    /**
     * 设置数据
     * @param array|string $key 键名或关联数组
     * @param mixed $value 值（当key为字符串时）
     * @return $this
     */
    public function set($key, $value = null): Model
    {
        if (is_array($key)) {
            foreach ($key as $k => $v) {
                $this->_data[$k] = $v;
            }
        } else {
            $this->_data[$key] = $value;
        }
        return $this;
    }

    /**
     * 获取数据
     * @param string|null $key 键名（可选）
     * @return mixed
     */
    public function get(?string $key = null)
    {
        if ($key === null) {
            return $this->_data;
        }
        return $this->_data[$key] ?? null;
    }

    /**
     * 创建数据
     * @param array|null $data 要创建的数据（可选）
     * @return bool|int
     */
    public function create(?array $data = null)
    {
        $data = $data ?? $this->_data;

        // 触发创建前事件
        if (!$this->trigger('beforeCreate', [$data])) {
            return false;
        }

        // 验证数据
        if (!$this->validate($data)) {
            return false;
        }

        // 执行插入
        $result = $this->_db->insert($data);

        // 触发创建后事件
        $this->trigger('afterCreate', [$data, $result]);

        return $result;
    }

    /**
     * 更新数据
     * @param array|null $data 要更新的数据（可选）
     * @param array|null $where 更新条件（可选）
     * @return bool|int
     */
    public function update(?array $data = null, ?array $where = null)
    {
        $data = $data ?? $this->_data;

        // 触发更新前事件
        if (!$this->trigger('beforeUpdate', [$data, $where])) {
            return false;
        }

        // 验证数据
        if (!$this->validate($data)) {
            return false;
        }

        // 设置条件
        if ($where) {
            $this->_db->where($where);
        }

        // 执行更新
        $result = $this->_db->update($data);

        // 触发更新后事件
        $this->trigger('afterUpdate', [$data, $where, $result]);

        return $result;
    }

    /**
     * 删除数据
     * @param array|null $where 删除条件（可选）
     * @return bool|int
     */
    public function delete(?array $where = null)
    {
        // 触发删除前事件
        if (!$this->trigger('beforeDelete', [$where])) {
            return false;
        }

        // 设置条件
        if ($where) {
            $this->_db->where($where);
        }

        // 执行删除
        $result = $this->_db->delete();

        // 触发删除后事件
        $this->trigger('afterDelete', [$where, $result]);

        return $result;
    }

    /**
     * 查找单条记录
     * @param array|null $where 查询条件（可选）
     * @return array
     */
    public function find(?array $where = null): array
    {
        if ($where) {
            $this->_db->where($where);
        }
        return $this->_db->find();
    }

    /**
     * 查找多条记录
     * @param array|null $where 查询条件（可选）
     * @return array
     */
    public function select(?array $where = null): array
    {
        if ($where) {
            $this->_db->where($where);
        }
        return $this->_db->select();
    }

    /**
     * 魔术方法，转发调用到数据库实例
     * @param string $method 方法名
     * @param array $args 方法参数
     * @return mixed
     */
    public function __call(string $method, array $args)
    {
        // 如果数据库实例有该方法，则转发调用
        if (method_exists($this->_db, $method)) {
            $result = call_user_func_array([$this->_db, $method], $args);
            // 如果返回的是数据库实例，返回当前模型实例以支持链式调用
            return $result === $this->_db ? $this : $result;
        }
        throw new \BadMethodCallException("方法 {$method} 不存在");
    }

    /**
     * 静态魔术方法，支持静态调用
     * @param string $method 方法名
     * @param array $args 方法参数
     * @return mixed
     */
    public static function __callStatic(string $method, array $args)
    {
        // 创建当前类的实例
        $instance = new static();
        // 调用实例方法
        return call_user_func_array([$instance, $method], $args);
    }

    /**
     * 析构函数
     */
    function __destruct()
    {
        // 清理资源
    }
}
